Widgets
In Flutter, widgets are the foundational building blocks of any application. Every part of a Flutter app, from the most basic UI elements like buttons to complex layouts, is built using widgets. Understanding widgets is key to mastering Flutter development. This page explores what widgets are, their types, and how to use them effectively in your apps.
What is a Widget?
A widget in Flutter is a description of part of a user interface. It can represent:
- A visible element, like a button, image, or text.
- An invisible element, like layout padding, alignment, or state management.
Widgets are designed to be immutable, meaning they cannot change directly once created. Instead, when the state of a widget changes, Flutter creates a new widget to reflect the changes.
Example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Widget Example',
home: Scaffold(
appBar: AppBar(
title: Text('Hello Flutter!'),
),
body: Center(
child: Text('This is a simple widget!'),
),
),
);
}
}
In this example:
Text
,Center
,AppBar
, andScaffold
are widgets that describe parts of the UI.- The
Text
widget renders a simple text string on the screen.
Types of Widgets
Flutter provides two main types of widgets:
- Stateless Widgets
- Stateful Widgets
1. Stateless Widgets
Stateless widgets do not change once created. They are immutable and only describe part of the UI without managing any state.
Example of a Stateless Widget:
import 'package:flutter/material.dart';
class MyStatelessWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16.0),
color: Colors.blue,
child: Text('I am a stateless widget'),
);
}
}
void main() {
runApp(MyStatelessWidget());
}
Here:
- The
Text
widget inside aContainer
is rendered and never changes. - Even if the widget is rebuilt due to UI updates, the widget’s structure remains the same.
Further Reading: StatelessWidget Documentation
2. Stateful Widgets
Stateful widgets can change over time. They manage state, which is the information (data) that can be read synchronously when the widget is built and might change during the widget's lifecycle.
Example of a Stateful Widget:
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stateful Widget Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Button pressed $_counter times'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment Counter'),
),
],
),
),
);
}
}
void main() {
runApp(MyStatefulWidget());
}
In this example:
- Stateful widgets manage the
_counter
value, which updates every time the button is pressed. - The
setState
method tells Flutter that the widget’s state has changed, triggering a rebuild to reflect the updated UI.
Further Reading: StatefulWidget Documentation
Widget Lifecycle
Stateful widgets have a lifecycle that allows developers to interact with their state at different points in time. Key lifecycle methods include:
initState()
: Called once when the state object is first created.build()
: Called every time the widget is rebuilt.dispose()
: Called when the state object is removed.
Example: Stateful Widget Lifecycle
class MyLifecycleWidget extends StatefulWidget {
_MyLifecycleWidgetState createState() => _MyLifecycleWidgetState();
}
class _MyLifecycleWidgetState extends State<MyLifecycleWidget> {
void initState() {
super.initState();
print("Widget Initialized");
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Widget Lifecycle Example'),
),
);
}
void dispose() {
print("Widget Disposed");
super.dispose();
}
}
Here, you can see how initState
and dispose
work at the start and end of the widget’s lifecycle.
Composition: Widgets Inside Widgets
One of Flutter's core principles is that everything is a widget. You can compose widgets together to create complex UIs. For example, building a form might include using TextField
, Button
, and Form
widgets inside a column layout.
Example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Nested Widgets')),
body: Column(
children: <Widget>[
Text('This is a text widget'),
ElevatedButton(
onPressed: () {},
child: Text('This is a button widget'),
),
],
),
),
);
}
}
In this example, the Column
widget is a layout widget that arranges its children vertically. The Text
and ElevatedButton
widgets are composed within the Column
.
Widgets in Flutter: Key Concepts
- Everything is a widget: Layouts, controls, text, and even the app’s root are widgets.
- Composition over inheritance: Instead of subclassing widgets, you compose widgets from other widgets.
- Declarative UI: In Flutter, the UI is rebuilt from scratch every time the app's state changes.
- Immutability: Widgets are immutable. Changes in the UI are handled by creating new widget trees.
Further Reading: Flutter Widget Catalog
Conclusion
Widgets are the heart of any Flutter app. By understanding how stateless and stateful widgets work, you can create dynamic, responsive UIs with ease. From basic UI components like buttons and text to custom layouts and state management, Flutter’s widget system is flexible, powerful, and essential for building cross-platform applications.
Next Steps:
- Explore the Flutter Widget Catalog to dive deeper into specific widgets.
- Try building your own stateless and stateful widgets to practice UI composition and state management.